﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Tweet.cs" company="Tobias Lekman">
//   Licensed under Microsoft Public License (Ms-PL). See lekman.codeplex.com/license for more information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace LekmanCom.Controls
{
    using System;
    using System.Globalization;
    using System.Linq;
    using System.Text.RegularExpressions;
    using System.Xml.Linq;

    /// <summary>
    /// The tweet.
    /// </summary>
    public class Tweet
    {
        #region Constants and Fields

        /// <summary>
        /// Property backer for <see cref="Date"/>.
        /// </summary>
        private string _date;

        /// <summary>
        /// Property backer for <see cref="Message"/>.
        /// </summary>
        private string _message;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Tweet"/> class.
        /// </summary>
        /// <param name="tweet">
        /// The tweet.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public Tweet(XContainer tweet)
        {
            if (tweet == null)
            {
                throw new ArgumentNullException("tweet");
            }

            XElement message = tweet.Element("text");
            if (message != null)
            {
                this.Message = this.MakeLink(message.Value, tweet);
            }

            XElement created = tweet.Element("created_at");
            if (created != null)
            {
                this.Date = created.Value;
            }
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the date of the update.
        /// </summary>
        /// <value>
        /// The date of the update.
        /// </value>
        public string Date
        {
            get
            {
                return this._date;
            }

            private set
            {
                this.FormatDate(value);
            }
        }

        /// <summary>
        /// Gets the message of the update.
        /// </summary>
        /// <value>
        /// The message of the update.
        /// </value>
        public string Message
        {
            get
            {
                return this._message;
            }

            private set
            {
                this._message = value.Replace(" & ", " &amp; ");
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Makes link from the tweet update.
        /// </summary>
        /// <param name="text">
        /// The tweet update.
        /// </param>
        /// <param name="node">
        /// The node.
        /// </param>
        /// <returns>
        /// The tweet update with links formatted as anchors.
        /// </returns>
        protected string MakeLink(string text, XContainer node)
        {
            var regx =
                new Regex(
                    "http://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", 
                    RegexOptions.IgnoreCase);
            MatchCollection matches = regx.Matches(text);

            text = matches.Cast<Match>().Aggregate(
                text, 
                (current, match) =>
                current.Replace(
                    match.Value, 
                    "<a href='" + match.Value.Replace("&amp;", "&").Replace("&", "&amp;") + "'>"
                    + GetDisplayUrl(match, node) + "</a>"));

            regx = new Regex("#[^ ]*", RegexOptions.IgnoreCase);
            matches = regx.Matches(text);

            text = matches.Cast<Match>().Aggregate(
                text, 
                (current, match) =>
                current.Replace(
                    match.Value, 
                    string.Format(
                        "<a href='http://twitter.com/#!/search?q={0}'>{1}</a>", 
                        match.Value.Replace("#", "%23"), 
                        match.Value)));

            regx = new Regex("@[a-zA-Z0-9][^ ]*", RegexOptions.IgnoreCase);
            matches = regx.Matches(text);

            return matches.Cast<Match>().Aggregate(
                text, 
                (current, match) =>
                current.Replace(
                    match.Value, 
                    string.Format(
                        "<a href='http://twitter.com/#!/{0}'>{1}</a>", 
                        match.Value.Replace("@", string.Empty), 
                        match.Value)));
        }

        /// <summary>
        /// Gets the display URL.
        /// </summary>
        /// <param name="match">
        /// The match.
        /// </param>
        /// <param name="date">
        /// The date.
        /// </param>
        /// <returns>
        /// The get display url.
        /// </returns>
        private static string GetDisplayUrl(Match match, XContainer date)
        {
            return
                (from x in date.Element("entities").Element("urls").Elements("url")
                 where x.Element("url").Value == match.Value
                 select x).FirstOrDefault().Element("display_url").Value;
        }

        /// <summary>
        /// Formats the date as lapsed time.
        /// </summary>
        /// <param name="value">
        /// The date string value.
        /// </param>
        private void FormatDate(string value)
        {
            DateTime date = DateTime.ParseExact(value, "ddd MMM dd HH:mm:ss zzz yyyy", CultureInfo.InvariantCulture);
            TimeSpan span = DateTime.Now.Subtract(date);

            if (span.TotalMinutes < 1)
            {
                this._date = string.Format("{0} second{1} ago", span.Seconds, span.Seconds == 1 ? string.Empty : "s");
                return;
            }

            if (span.TotalHours < 1)
            {
                this._date = string.Format("{0} minute{1} ago", span.Minutes, span.Minutes == 1 ? string.Empty : "s");
                return;
            }

            if (span.TotalDays < 1)
            {
                this._date = string.Format("{0} hour{1} ago", span.Hours, span.Hours == 1 ? string.Empty : "s");
                return;
            }

            this._date = string.Format("{0} day{1} ago", span.Days, span.Days == 1 ? string.Empty : "s");
        }

        #endregion
    }
}